package org.raymond.icloud.common.starter.webFilter;

import com.alibaba.fastjson.JSONObject;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.raymond.icloud.common.core.constants.CommonConstants;
import org.raymond.icloud.common.core.constants.ErrorCode;
import org.raymond.icloud.common.core.constants.CacheConstants;
import org.raymond.icloud.common.core.exception.BaseException;
import org.raymond.icloud.common.core.security.*;
import org.raymond.icloud.common.core.security.model.UsernamepasswordCertification;
import org.raymond.icloud.common.core.util.ApplicationContextUtils;
import org.raymond.icloud.common.core.util.FilterUtil;
import org.raymond.icloud.common.core.util.RedisUtil;
import org.raymond.icloud.common.core.util.RequestUtil;
import org.springframework.context.ApplicationContext;
import org.springframework.web.servlet.HandlerExceptionResolver;

import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletRequestWrapper;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.Map;

@Slf4j
public class LoginFilter extends AbstractFilter {
    private static final String adminLoginPath = "/admin/user/login";

    private HandlerExceptionResolver resolver;
    private Authentication authentication;
    private RedisUtil redisUtil;

    @Override
    protected void initFilterBean() throws ServletException {
        ApplicationContext ctx = ApplicationContextUtils.getApplicationContext();
        resolver = ctx.getBean("handlerExceptionResolver", HandlerExceptionResolver.class);
        authentication = ctx.getBean("authenticationService", Authentication.class);
        redisUtil = ctx.getBean("redisUtil", RedisUtil.class);
    }

    @Override
    protected void doFilterSub(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
        String requestUri = RequestUtil.getRelativePath(request);
        if(!isLogin(requestUri)){
            filterChain.doFilter(request, response);
            return;
        }

        String token = RequestUtil.resolve(request);
        if (StringUtils.isNotBlank(token)) {
            filterChain.doFilter(request, response);
            return;
        }

        String requestString = FilterUtil.getRequestString(request);
        Map loginMap = JSONObject.parseObject(requestString, Map.class);
        log.debug("{}", loginMap);
        String loginType = String.valueOf(loginMap.get(CommonConstants.LOGIN_TYPE_KEY));
        String username = String.valueOf(loginMap.get(CommonConstants.LOGIN_USERNAME_KEY));
        String password = String.valueOf(loginMap.get(CommonConstants.LOGIN_PASSWORD_KEY));
        if(StringUtils.isBlank(username) || StringUtils.isBlank(password)){
            resolver.resolveException(request, response, null, new BaseException(ErrorCode.UNAUTHORIZED, "username or password is blank"));
            return;
        }

        UsernamepasswordCertification certification = new UsernamepasswordCertification(username, password);
        HttpServletRequestWrapper requestWrapper = null;
        try {
            boolean result = authentication.authenticate(certification);
            if(!result) throw new BaseException(ErrorCode.UNAUTHORIZED, "certification authenticate failed");
            requestWrapper = addTokenWrapper(certification, request);
        } catch (BaseException e) {
            resolver.resolveException(request, response, null, e);
            return;
        }
        filterChain.doFilter(requestWrapper, response);
    }

    private boolean isLogin(String requestPath){
        return adminLoginPath.equalsIgnoreCase(requestPath);
    }

    private HttpServletRequestWrapper addTokenWrapper(UsernamepasswordCertification certification, HttpServletRequest request){
        HttpServletRequestWrapper requestWrapper = new HttpServletRequestWrapper(request) {
            @Override
            public String getHeader(String name){
                String headerVal = super.getHeader(name);
                if(name.equals(CacheConstants.KEY_TOKEN) && StringUtils.isBlank(headerVal)){
                    headerVal = CacheConstants.KEY_TOKEN_PREFIX + certification.getCertification();
                }
                return headerVal;
            }
        };
        return requestWrapper;
    }
}
